package com.corewillsoft.fireflies.gamelaws;

import com.corewillsoft.fireflies.gameobjects.AreaObject;
import com.corewillsoft.fireflies.gameobjects.Firefly;
import com.corewillsoft.fireflies.gameobjects.GameObject;
import com.corewillsoft.fireflies.gameobjects.obstacles.Fan;

public class FanFireflyLaw extends BaseLaw {
	private static final float MAX_DISTANCE = 180f;	//maximal distance (by normal) from fan, where fan has influence on firefly
	private static final float FAN_FORCE = 2.1f;  	//maximal force of fan (in the center of aperture, in zero-length from fan)

	@Override
	public void applicate(GameObject gameObject) {
		if (gameObject instanceof Firefly)
			processFirefly((Firefly) gameObject);
	}

	/**
	 * Changes the speed of firefly according to the fan influence;
	 * only x-component of firefly speed is changed (fan blows strictly along normal of its aperture)
	 * @param firefly - firefly fan is acting on
	 */
	private void processFirefly(Firefly firefly) {
		GameObject fan = getParent();

		if (!(fan instanceof Fan))
			return;

		float fanAperture = ((Fan) fan).getAperture();

		//distance between firefly and fan aperture
		float dx = firefly.getX() - fan.getX();

		//distance between firefly and perpendicular drawn from the center of fan aperture
		float dy = firefly.getY() - (fan.getY() + (fanAperture / 2));

		//calculation of the fan force
	    float fanForceX = getForceX(dx, dy, fanAperture);

		//update the speed of firefly (only x-component of the speed)
		firefly.setSpeed(firefly.getSpeedX() + fanForceX, firefly.getSpeedY());
	}

	/**
	 * Initializes influence area for fan-firefly law;
	 * fan can influence on firefly in the rectangle with height = fan aperture
	 * and width = maximal distance of influence ({@link FanFireflyLaw#MAX_DISTANCE})
	 */
	@Override
	public void initializeInfluenceArea() {
		if (parent == null) {
			setArea(null);
		} else if (!(parent instanceof Fan)) {
		    setArea(null);
		} else {
			Fan fan = (Fan) parent;

			float fanX = fan.getX();
			float fanY = fan.getY();
			float fanAperture = fan.getY();

			AreaObject influenceArea = new AreaObject(fanX, fanX + MAX_DISTANCE, fanY, fanY + fanAperture);
			setArea(influenceArea);
		}
	}

	/**
	 * Calculates x-component of force of fan (normalized to maximal value) acting on firefly
	 * @param dx - distance by normal between fan aperture and firefly
	 * @param dy - shortest distance between perpendicular drawn from the enter of fan aperture and firefly
	 * @param aperture - aperture of the fan
	 * @return force of fan, normalized to maximal value {@link FanFireflyLaw#FAN_FORCE}
	 */
	private float getForceX(float dx, float dy, float aperture) {
		return (FAN_FORCE * getForceXByDX(dx) * getForceXByDY(dy, aperture));
	}

	/**
	 * Calculates normalized (to 1) x-component of fan force, depending only on dx
	 * (distance by normal between fan aperture and firefly)
	 * @param dx - distance by normal between fan aperture and firefly
	 * @return component of fan force, normalized to 1
	 */
	private float getForceXByDX(float dx) {
		if ((dx < 0) || (dx > MAX_DISTANCE)) {
			return 0;
		}

		return (1 - dx / MAX_DISTANCE);
	}

	/**
	 * Calculates normalized (to 1) x-component of fan force, depending only on dy
	 * (shortest distance between perpendicular drawn from the enter of fan aperture
	 * and firefly)
	 * @param dy - shortest distance between perpendicular drawn from the enter of fan aperture and firefly
	 * @param aperture - aperture of the fan
	 * @return component of fan force, normalized to 1
	 */
	private float getForceXByDY(float dy, float aperture) {
		if (Math.abs(dy) > aperture)
			return 0;

		return (1 - ((dy * dy) / (aperture * aperture)));
	}

	@Override
	public LawType getType() {
		return LawType.FAN_FIREFLY;
	}
}
